﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Data.Linq;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;
using Ooganizer.Launcher;
using Ooganizer.Closer;
using Ooganizer.Platform.Win32;
using Ooganizer.Server;

namespace Ooganizer
{
    namespace API
    {
        [Serializable]
        public class Workspace
        {
            public Workspace(Guid workspaceId)
            {
                // TODO Need to create context/BO or context/atomic action (storing index here instead of DAL)
                m_workspaceId = workspaceId;
            }

            private bool SaveSnapshot(Image snapshot, OoganizerDAL.Artifact artifactDAL)
            {
                string path = Platform.Env.GetSnapshotDirectory();

                path += @"\";
                path += artifactDAL.Id;
                path += ".png";

                // save to file
                snapshot.Save(path);

                // save to the artifact
                artifactDAL.Snapshot_Path = path;

                return true;
            }

            public bool AddArtifact(string name, string location, Image snapshotImage)
            {
                bool bSuccess = false;

                try
                {
                    OoganizerDAL.Artifact artifactDAL = null;

                    // Make sure that this artifact does not already exist (e.g. referenced by another workspace)                    
                    if (!m_oogyDB.Artifacts.Where(a => a.Location == location).Any())
                    {
                        artifactDAL = new OoganizerDAL.Artifact();

                        artifactDAL.Name = name;
                        artifactDAL.Id = Guid.NewGuid();
                        artifactDAL.Location = location;
                        artifactDAL.Launcher_id = LauncherFactory.ResolveLauncherId(location);

                        SaveSnapshot(snapshotImage, artifactDAL);
                    }
                    else
                    {
                        artifactDAL = m_oogyDB.Artifacts.Where(a => a.Location == location).Single();
                    }

                    // Create WorkspaceArtifact for this workspace
                    OoganizerDAL.WorkspaceArtifact wsArtifactDAL = new OoganizerDAL.WorkspaceArtifact();

                    wsArtifactDAL.Artifact = artifactDAL;
                    wsArtifactDAL.Workspace = m_workspaceDal;

                    // Initialize the window settings
                    // Assign current window dimensions
                    wsArtifactDAL.Window_pos_x = 0;
                    wsArtifactDAL.Window_pos_y = 0;

                    // Screen dimensions are based on the screen that contains the top left point of the window (so we can handle multiple screens)
                    System.Drawing.Point topLeft = new System.Drawing.Point(0, 0);
                    wsArtifactDAL.Screen_resolution_horizontal = System.Windows.Forms.Screen.FromPoint(topLeft).Bounds.Width;
                    wsArtifactDAL.Screen_resolution_vertical = System.Windows.Forms.Screen.FromPoint(topLeft).Bounds.Height;

                    wsArtifactDAL.Window_size_horizontal = wsArtifactDAL.Screen_resolution_horizontal / 2;
                    wsArtifactDAL.Window_size_vertical = wsArtifactDAL.Screen_resolution_vertical  / 2;

                    m_workspaceDal.WorkspaceArtifacts.Add(wsArtifactDAL);
                    
                    m_oogyDB.SubmitChanges();

                    bSuccess = true;
                }
                catch (Exception e) 
                {
                    Log.Error(string.Format("{0}() - Unable to add artifact at location {1}",
                                            System.Reflection.MethodBase.GetCurrentMethod().Name,
                                            location),
                              e);
                }

                return bSuccess;
            }

            public bool RemoveArtifact(string location)
            {
                bool bSuccess = false;

                try
                {
                    var workspaceArtifacts =
                       from wa in m_workspaceDal.WorkspaceArtifacts
                       where wa.Artifact.Location == location
                       select wa;

                    foreach (var workspaceArtifact in workspaceArtifacts)
                    {
                        // TODO: Does this actually delete the artifact objects???
                        m_workspaceDal.WorkspaceArtifacts.Remove(workspaceArtifact);
                    }

                    m_oogyDB.SubmitChanges();

                    bSuccess = true;
                }
                catch (Exception e)
                {
                    Log.Error(string.Format("{0}() - Unable to remove artifact at location {1}",
                                            System.Reflection.MethodBase.GetCurrentMethod().Name,
                                            location),
                              e);
                    bSuccess = false;
                }

                return bSuccess;
            }

            private Dictionary<IntPtr, Guid> m_artifactUIMapping =  new Dictionary<IntPtr, Guid>();
            
            public bool Load()
            {
                var workspaceArtifacts =
                   from wa in m_workspaceDal.WorkspaceArtifacts
                   select wa;

                foreach (var workspaceArtifact in workspaceArtifacts)
                {
                    ILauncher launcher = LauncherFactory.CreateLauncher(workspaceArtifact.Artifact.Launcher_id);

                    IntPtr hWnd = launcher.Launch(workspaceArtifact.Artifact.Location);

                    if (hWnd != IntPtr.Zero)
                    {
                        // Insert into UI map
                        m_artifactUIMapping.Add(hWnd, workspaceArtifact.Artifact_id);

                        Win32Functions.SetWindowPos(hWnd, (IntPtr)WindowPos.HWND_NOTOPMOST, workspaceArtifact.Window_pos_x, workspaceArtifact.Window_pos_y, workspaceArtifact.Window_size_horizontal, workspaceArtifact.Window_size_vertical, (int)SetWindowPosFlags.SWP_SHOWWINDOW);
                    }
                }

                return true;
            }

            public bool Unload()
            {
                bool bSuccess = true;

                // Call Close App on Closer
                CloserDispatch closer = new CloserDispatch();

                foreach (KeyValuePair<IntPtr, Guid> uiMapping in m_artifactUIMapping)
                {
                    if (!SetWorkspaceArtifactDimensions(uiMapping.Key, m_workspaceDal.Id, uiMapping.Value))
                    {
                        // Log a warning here ... no need to fail though, since we can just launch it in 
                        //        the top left of the first screen display by default
                        Log.WarnFormat("{0}() - Unable to set artifact dimensions for artifact {1}", 
                                       System.Reflection.MethodBase.GetCurrentMethod().Name, 
                                       uiMapping.Value); 
                    }

                    OoganizerDAL.WorkspaceArtifact workspaceArtifact = m_workspaceDal.WorkspaceArtifacts.Where(wa => wa.Artifact_id == uiMapping.Value && wa.Workspace_id == m_workspaceDal.Id).Single();

                    if (!closer.CloseApp(uiMapping.Key, workspaceArtifact.Artifact.Location))
                        bSuccess = false;
                }

                m_artifactUIMapping.Clear();

                return bSuccess;
            }

            public bool Hide()
            {
                foreach (KeyValuePair<IntPtr, Guid> uiMapping in m_artifactUIMapping)
                {
                    Win32Functions.ShowWindow(uiMapping.Key, (int)WindowAction.SW_HIDE);
                }

                return true;
            }

            public bool Show()
            {
                foreach (KeyValuePair<IntPtr, Guid> uiMapping in m_artifactUIMapping)
                {
                    Win32Functions.ShowWindow(uiMapping.Key, (int)WindowAction.SW_SHOW);
                }

                return true;
            }

            private bool SetWorkspaceArtifactDimensions(IntPtr hWnd, Guid workspaceId, Guid artifactId)
            {
                RECT rect = new RECT();

                bool bSuccess = false;

                if (Win32Functions.GetWindowRect(hWnd, out rect))
                {
                    try
                    {
                        OoganizerDAL.WorkspaceArtifact workspaceArtifact = m_workspaceDal.WorkspaceArtifacts.Where(wa => wa.Artifact_id == artifactId && wa.Workspace_id == workspaceId).Single();

                        // Assign current window dimensions
                        workspaceArtifact.Window_pos_x = rect.left;
                        workspaceArtifact.Window_pos_y = rect.top;
                        workspaceArtifact.Window_size_horizontal = Math.Abs(rect.right - rect.left);
                        workspaceArtifact.Window_size_vertical = Math.Abs(rect.top - rect.bottom);

                        // Screen dimensions are based on the screen that contains the top left point of the window (so we can handle multiple screens)
                        System.Drawing.Point topLeft = new System.Drawing.Point(rect.left, rect.top);
                        workspaceArtifact.Screen_resolution_horizontal = System.Windows.Forms.Screen.FromPoint(topLeft).Bounds.Width;
                        workspaceArtifact.Screen_resolution_vertical = System.Windows.Forms.Screen.FromPoint(topLeft).Bounds.Height;

                        m_oogyDB.SubmitChanges();

                        bSuccess = true;
                    }
                    catch (Exception e)
                    {
                        Log.Warn(string.Format("{0}() - Unable to set dimensions for artifact {1}",
                                               System.Reflection.MethodBase.GetCurrentMethod().Name,
                                               artifactId),
                                 e);
                    }
                }

                return bSuccess;
            }



            public ArtifactsCollection Artifacts
            {
                get { return new ArtifactsCollection(m_workspaceDal.Id); }
            }

            public string Name
            {
                get { return m_workspaceDal.Name; }
            }

            private Guid m_workspaceId;

            private OoganizerDAL.Workspace _workspaceDal;
            private OoganizerDAL.Workspace m_workspaceDal
            {
                get
                {
                    if (_workspaceDal == null)
                    {
                        _workspaceDal = m_oogyDB.Workspaces.Where(w => w.Id == m_workspaceId).Single();
                    }

                    return _workspaceDal;
                }
            }

            private OoganizerDAL.OogyDB _oogyDB;
            private OoganizerDAL.OogyDB m_oogyDB
            {
                get
                {
                    if (_oogyDB == null)
                    {
                        _oogyDB = new OoganizerDAL.OogyDB();
                        _oogyDB.Log = Console.Out;
                    }

                    return _oogyDB;
                }
            }

            #region log4net declaration

            private log4net.ILog Log
            {
                get
                {
                    if (_logWrapper == null)
                    {
                        _logWrapper = new LogServerClientWrapper(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
                    }

                    return _logWrapper.Log;
                }
            }

            private LogServerClientWrapper _logWrapper;

            #endregion

        }

        [ComVisible(true)]
        [GuidAttribute("34B212B3-4C56-4ec8-9E00-1F59D282E76B")]
        [ClassInterface(ClassInterfaceType.None)]
        public class ArtifactsCollection : IEnumerable<OoganizerDAL.Artifact>
        {
            public ArtifactsCollection(Guid workspaceId)
            {
                m_workspaceId = workspaceId;
            }

            public IEnumerator<OoganizerDAL.Artifact> GetEnumerator()
            {
                // DataContext takes a connection string 
                OoganizerDAL.OogyDB db = new OoganizerDAL.OogyDB();

                // Query for artifacts in this workspace
                var artifacts =
                   from wa in db.WorkspaceArtifacts
                   where wa.Workspace_id == m_workspaceId
                   select wa.Artifact;


                return ((IEnumerable<OoganizerDAL.Artifact>)artifacts).GetEnumerator();
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            private Guid m_workspaceId;
        }
    }
}
